using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ContentOverContainer.Common;
using ContentOverContainer.Common.Converter;
using Roslyn.Compilers.CSharp;

namespace ContentOverContainer.Converter
{
    public static class EqualsConverter
    {
        /// <summary>
        /// Converts the assign expression. (R-Ident1 , R-Ident2, R-Ident3, R-Ident4)
        /// </summary>
        /// <param name="pAssignExpression">The binary (assign) expression.</param>
        /// <returns></returns>
        /// <author>Harald Binkle</author>
        public static ChangeNodeInfo ConvertEqualsExpression(BinaryExpressionSyntax pEqualsExpression, ConverterContext pConverterData)
        {
            var value = pEqualsExpression.Right;
            var member = pEqualsExpression.Left;

            TypeSyntax valueType = null;
            var valueName = value.FirstAncestorOrSelf<SimpleNameSyntax>();
            var valueDecl = valueName.GetMemberDeclaration(pConverterData.SemanticModel);
            if (valueDecl == null) //case of properties we are getting null "value"
            {
                if (valueName.ToString().Equals("value", StringComparison.OrdinalIgnoreCase))
                {
                    valueDecl = value.FirstAncestorOrSelf<PropertyDeclarationSyntax>();
                }
            }
            var valueMultiplicityKind = valueDecl.GetMultiplicityKind();
            valueType = valueDecl.GetDeclarationType();
            if (valueMultiplicityKind == MultiplicityKind.Any)
                valueType = valueDecl.GetMultiplicityAttributes().GetMultiplicityType(valueType, MultiplicityKind.Any);


            var memberName = member.FirstAncestorOrSelf<SimpleNameSyntax>();
            var memberDecl = memberName.GetMemberDeclaration(pConverterData.SemanticModel);
            var memberMultiplicityKind = memberDecl.GetMultiplicityKind();
            var memberType = memberDecl.GetDeclarationType();
            if (memberMultiplicityKind == MultiplicityKind.Any)
                memberType = memberDecl.GetMultiplicityAttributes().GetMultiplicityType(memberType, MultiplicityKind.Any);

            List<ParameterPair> parameter = new List<ParameterPair>();         
            //build Add statement
            StatementSyntax invocationAddStatement = null;

            //R-Ident1
            if (memberMultiplicityKind == MultiplicityKind.Option && valueMultiplicityKind == MultiplicityKind.Option)
            {
                return new ChangeNodeInfo(pEqualsExpression);
            }
            //R-Ident2
            if (memberMultiplicityKind == MultiplicityKind.Any && valueMultiplicityKind == MultiplicityKind.Option)
            {
                invocationAddStatement = Syntax.ParseStatement(String.Format("return {0}.Count == 0 ? {1} == null : {0}.First().Equals({1}); " + Environment.NewLine, member, value));
            }
            //R-Ident3
            if (memberMultiplicityKind == MultiplicityKind.Option && valueMultiplicityKind == MultiplicityKind.Any)
            {//inverted values of R-Ident2
                invocationAddStatement = Syntax.ParseStatement(String.Format("return {0}.Count == 0 ? {1} == null : {0}.First().Equals({1}); " + Environment.NewLine,  value, member));
            }
            //R-Ident4
            if (memberMultiplicityKind == MultiplicityKind.Any && valueMultiplicityKind == MultiplicityKind.Any)
            {   //Enumarable.Exept: Produces the set difference of two sequences by using the default equality comparer to compare values.
                invocationAddStatement = Syntax.ParseStatement(String.Format("return {0}.Count == {1}.Count && {0}.Except({1}).Count() == 0;" + Environment.NewLine, member, value));
            }

            var methodBody = Syntax.Block(invocationAddStatement);

            TypeSyntax exprType = pConverterData.MultiplicityInfo.Type;

            var composition = MethodComposer.BuildMethod("Equal", methodBody, parameter, "bool");
            var result = new ChangeNodeInfo(composition.InvocationCall);
             var methodSubstitution = new ChangeNodeMethodSubstitution()
            {
                Class = pEqualsExpression.FirstAncestorOrSelf<ClassDeclarationSyntax>(),
                MethodDeclaration = composition.MethodDecl
            };
            result.MethodSubstitutionList.Add(methodSubstitution);
            return result;
        }
    }
}
